package com.roamer.audition.web.common.handle;

import com.roamer.rest.IErrorCode;
import com.roamer.rest.RestResult;
import com.roamer.rest.exception.BusinessException;
import com.roamer.rest.exception.ParameterInvalidItem;
import com.roamer.rest.exception.ValidateConvert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.List;
import java.util.Set;

/**
 * 控制层 统一异常处理器
 *
 * @author roamer
 * @version V1.0
 * @date 2019/1/21 16:35
 */
@Slf4j
@Order(Integer.MIN_VALUE)
@ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {


    /**
     * 缺少参数异常
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        RestResult<?> restResult = RestResult.failure(IErrorCode.C40001, new ParameterInvalidItem().setField(ex.getParameterName()).setMessage(ex.getMessage()));
        return super.handleExceptionInternal(ex, restResult, headers, status, request);
    }


    /**
     * 参数解析异常
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40002), headers, status, request);
    }


    /**
     * 参数绑定异常
     * <p>
     * 处理器参数类型不匹配
     * </p>
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResultBean}
     */
    @Override
    protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40003), headers, status, request);
    }

    /**
     * 参数绑定异常
     * <p>
     * 处理器参数类型不匹配
     * </p>
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResultBean}
     */
    @Override
    protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40003), headers, status, request);
    }

    /**
     * 参数验证异常
     * <p>
     * 非{@link org.springframework.web.bind.annotation.RequestBody} 标注的参数校验异常时
     * </p>
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<ParameterInvalidItem> parameterInvalidItemList = ValidateConvert.convertToInvalidItemList(
                ex.getBindingResult());
        RestResult<List<ParameterInvalidItem>> restResult = RestResult.failure(IErrorCode.C40004, parameterInvalidItemList);
        return super.handleExceptionInternal(ex, restResult, headers, status, request);
    }

    /**
     * 参数验证异常
     * <p>
     * <p>
     * {@link org.springframework.web.bind.annotation.RequestBody} 标注的参数校验异常时
     * </p>
     * 400 - Bad Request
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<ParameterInvalidItem> parameterInvalidItemList = ValidateConvert.convertToInvalidItemList(
                ex.getBindingResult());
        RestResult<List<ParameterInvalidItem>> restResult = RestResult.failure(IErrorCode.C40004, parameterInvalidItemList);
        return super.handleExceptionInternal(ex, restResult, headers, status, request);
    }


    /**
     * 资源未找到异常
     * 404 - Not Found
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40400), headers, status, request);
    }

    /**
     * 不支持当前请求方法
     * 405 - Method Not Allowed
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        pageNotFoundLogger.warn(ex.getMessage());

        Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
        if (!CollectionUtils.isEmpty(supportedMethods)) {
            headers.setAllow(supportedMethods);
        }
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40500), headers, status, request);
    }


    /**
     * 请求的格式不可得
     * 406 - Not Acceptable
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C40600), headers, status, request);
    }

    /**
     * 不支持当前媒体类型
     * 415 - Unsupported Media Type
     *
     * @param ex      异常信息
     * @param request 请求
     * @param status  HttpStatus
     * @return {@code ResponseEntity<Object>}
     */
    @Override
    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.C41500), headers, status, request);
    }

    /**
     * 处理通用自定义业务异常
     *
     * @param ex      异常信息
     * @param request 请求
     * @return {@code ResponseEntity<Object>}
     */
    @ExceptionHandler(BusinessException.class)
    protected ResponseEntity<Object> handleBusinessException(BusinessException ex, WebRequest request) {
        log.warn("handleBusinessException start, exception:{}, caused by: {}", ex.getClass(), ex.getErrorCode());
        HttpStatus status = ex.getErrorCode().httpStatus();
        HttpHeaders headers = new HttpHeaders();
        RestResult<?> restResult = RestResult.failure(ex.getErrorCode(), ex.getData());
        return super.handleExceptionInternal(ex, restResult, headers, status, request);
    }

    /**
     * 处理运行时系统异常
     *
     * @param ex      异常信息
     * @param request 请求
     * @return {@code ResultBean<?>}
     */
    @ExceptionHandler(RuntimeException.class)
    protected ResponseEntity<Object> handleRunTimeException(RuntimeException ex, WebRequest request) {
        log.error("handleRunTimeException start, uri:{}, caused by: ", request.getContextPath(), ex);
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        HttpHeaders headers = new HttpHeaders();
        //TODO 可通过邮件、微信公众号等方式发送信息至开发人员、记录存档等操作
        return super.handleExceptionInternal(ex, RestResult.failure(IErrorCode.ERROR), headers, status, request);
    }
}
